#include "types.h"
#include "platform.h"

/*
 * The UART control registers are memory-mapped at address UART0. 
 * This macro returns the address of one of the registers.
 */
#define UART_REG(reg) ((volatile uint8_t *)(UART0 + reg))

/*
 * Reference
 * [1]: TECHNICAL DATA ON 16550, http://byterunner.com/16550.html
 */

/*
 * UART control registers map. see [1] "PROGRAMMING TABLE"
 * note some are reused by multiple functions
 * 0 (write mode): THR/DLL
 * 1 (write mode): IER/DLM
 */
#define RHR 0	// Receive Holding Register接收保持寄存器 (read mode)CPU读取已接收到的 1 字节数据
#define THR 0	// Transmit Holding Register发送保持寄存器 (write mode)CPU写入要发送的 1 字节数据
#define DLL 0	// LSB of Divisor Latch除数锁存器低字节 (write mode)波特率除数的低 8 位（用于设置波特率）DLAB（LCR BIT-7）控制配置DLL模式还是THR/RHR模式
#define IER 1	// Interrupt Enable Register中断使能寄存器 (write mode)打开/关闭各类 UART 中断，DLAB（LCR BIT-7）控制配置模式
#define DLM 1	// MSB of Divisor Latch除数锁存器高字节 (write mode)波特率除数的高 8 位
#define FCR 2	// FIFO Control Register FIFO控制寄存器(write mode)
#define ISR 2	// Interrupt Status Register中断状态寄存器 (read mode)
#define LCR 3	// Line Control Register线路控制寄存器
#define MCR 4	// Modem Control Register调制解调器控制寄存器
#define LSR 5	// Line Status Register线路状态寄存器
#define MSR 6	// Modem Status Register调制解调器状态寄存器
#define SPR 7	// ScratchPad Register临时寄存器

/*
 * POWER UP DEFAULTS 上电默认值
 * IER 中断使能寄存器 = 0: TX/RX holding register interrupts are both disabled
 * ISR 中断状态寄存器 = 1: no interrupt pending
 * LCR 线路控制寄存器 = 0
 * MCR 调制解调器控制寄存器 = 0
 * LSR 线路状态寄存器 = 60 HEX
 * MSR 调制解调器状态寄存器 = BITS 0-3 = 0, BITS 4-7 = inputs
 * FCR FIFO控制寄存器 = 0
 * TX 发送端 = High
 * OP1 选项 1= High
 * OP2 选项 2= High
 * RTS 请求发送 = High
 * DTR 数据终端就绪 = High
 * RXRDY 接收就绪 = High
 * TXRDY 发送就绪 = Low
 * INT 中断 = Low
 */

/*
 * LINE STATUS REGISTER (LSR)
 * LSR BIT 0:
 * 0 = no data in receive holding register or FIFO.
 * 1 = data has been receive and saved in the receive holding register or FIFO.
 * ......
 * LSR BIT 5:
 * 0 = transmit holding register is full. 16550 will not accept any data for transmission.
 * 1 = transmitter hold register (or FIFO) is empty. CPU can load the next character.
 * ......
 */
#define LSR_RX_READY (1 << 0)	//数据已接收并存储在接收保持寄存器中
#define LSR_TX_IDLE  (1 << 5)	//发送保持寄存器为空

#define uart_read_reg(reg) (*(UART_REG(reg)))
#define uart_write_reg(reg, v) (*(UART_REG(reg)) = (v))

void uart_init()
{
	/* disable interrupts. */
	uart_write_reg(IER, 0x00);

	/*
 	 * 设置波特率。这里演示如何配置除数，但在我们的场景（QEMU-virt）下，
 	 * 这实际上并不会产生真正效果。
     *
     * 注意：除数锁存器 DLL（低字节）和 DLM（高字节）与接收/发送寄存器、
     * 中断使能寄存器共用相同的基址。要让这些基址映射到“除数锁存器”，
     * 需要将线路控制寄存器（LCR）的第 7 位（DLAB，Divisor Latch Access Bit）
     * 置为 1 来“打开除数锁存器”访问。
     *
     * 关于波特率的取值，请参见文献[1] 的 “BAUD RATE GENERATOR PROGRAMMING TABLE”。
     * 当使用 1.8432 MHz 晶振并选择 38.4K 波特率时，对应的除数是 3。
     * 由于除数寄存器是 16 位（两个字节），所以需要把 3（0x0003）
     * 拆分成两个字节：低字节写入 DLL，高字节写入 DLM。
     */

	uint8_t lcr = uart_read_reg(LCR);
	uart_write_reg(LCR, lcr | (1 << 7));
	uart_write_reg(DLL, 0x03);
	uart_write_reg(DLM, 0x00);

	/*
	 * 继续设置异步数据通信格式：
	 * - 数据位数：8 位
	 * - 停止位数：当数据位为 8 位时为 1 位
	 * - 无校验位（no parity）
	 * - 关闭断续控制（no break control）
	 * - 关闭除数锁存（禁用波特率锁存）
	 */
	lcr = 0;
	uart_write_reg(LCR, lcr | (3 << 0));
}

int uart_putc(char ch)
{
	while ((uart_read_reg(LSR) & LSR_TX_IDLE) == 0);
	return uart_write_reg(THR, ch);
}

void uart_puts(char *s)
{
	while (*s) {
		uart_putc(*s++);
	}
}

